//
//  Feed.m
//
// RE::comments
// class Feed implementation

#import "Feed.h"
#import "Channel.h"
#import "Post.h"
#import "ASIHTTPRequest.h"

@implementation Feed

@synthesize feedURL;
@synthesize feedRequest;
@synthesize feedChannel;
@synthesize feedPosts;
@synthesize currentElement;
@synthesize currentElementData;

// Official names of the events we want the Notification Center to tell our delegate about
NSString *kFeederReloadCompletedNotification = @"kFeederReloadCompletedNotification";
NSString *kFeederReloadFailedNotification = @"kFeederReloadFailedNotification";

#pragma mark -
#pragma mark === Initialization and release ===
#pragma mark -

// initWithURL:
// initialize this Feed obj with the URL from whence the Stuff will flow off the Web, and set up
//    an array of Post objects to hold the individual item / Post chunks of the flow
-(id)initWithURL:(NSURL *)sourceURL {
	
	if (self = [super init]) {
		
		self.feedURL = sourceURL;
		self.feedPosts = [[NSMutableArray alloc] init];

	}
	
	return self;
}

// dealloc:
// get rid of everything that we dynamically allocated
- (void)dealloc {
    
	[currentElementData release];
	[feedPosts release];
	[feedChannel release];
	[feedRequest release];
	[feedURL release];
	
	[super dealloc];
}

#pragma mark -
#pragma mark === ASIHTTPRequest delegates ===
#pragma mark -

// requestFinished:
// Oh Boy! We got sthg off the Web! ... via our ASIHTTPRequest
- (void)requestFinished:(ASIHTTPRequest *)request {
	
	// grab the data from the net request response
	NSData *responseData = [request responseData];
	
	// alloc a new parser obj and init it w/ retrieved glob of data
	NSXMLParser *parser = [[NSXMLParser alloc] initWithData:responseData];
	
	// let ME be the parser's delegate --- tell parser to send ME messages about what it's parsing
	[parser setDelegate:self];
	
	// did parser find something in the request result gunk?
	if ([parser parse]) {

		// then tell the Notification Center that the download of info was successful, and therefore
		//    the Not. Cntr should tell any observers that are watching that there's been an update
		//    --> Note: the FeedViewCtrlr will need to *register* with the Not. Cntr that it wants to
		//           receive notifications when these postings occur!
		[[NSNotificationCenter defaultCenter] 
		 postNotificationName:kFeederReloadCompletedNotification
		 object:nil];
		
	}
	
	// let go of the dynamically allocated parser object
	[parser release];
}

// requestFailed:
// wah. We didn't get anything. Some error occurred.
- (void)requestFailed:(ASIHTTPRequest *)request {
	
	// find out what error occurred and print it to the console
	NSError *error = [request error];
	NSLog(@"requestFailed: %@", [error localizedDescription]);
	
	// alert the media; i.e., tell the Notificatino Center about the error (...so it can pass it on to 
	//    whoever is interested)
	[[NSNotificationCenter defaultCenter] 
						postNotificationName:kFeederReloadFailedNotification
						object:nil];
	
}

#pragma mark -
#pragma mark === NSXMLParserDelegate methods ===
#pragma mark -

// names of specific elements that we want to detect as we sift thru the XML gunk
static NSString * const kChannelElementName = @"channel";
static NSString * const kItemElementName = @"item";


// parser:didStartElement:blahblahblah
// XML PARSER DELEGATE METHOD:
// detect the start of a specific XML element
//    --> here, for now, we only care about the *element name*
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
               namespaceURI:(NSString *)namespaceURI
               qualifiedName:(NSString *)qName
	           attributes:(NSDictionary *)attributeDict {
	
	// did we find a new <channel> starting tag amidst the XML goop?
	if ([elementName isEqualToString:kChannelElementName]) {
		
		// if so, make a new Channel object and set up its state
		Channel *channel = [[Channel alloc] init];
		self.feedChannel = channel;
		self.currentElement = channel;  // ***
		[channel release];
		return; // ...and we're done, for now
	}
	
	// or maybe did we find a new <item> starting tag? which for us, signals the start of a Post?
	if ([elementName isEqualToString:kItemElementName]) {
		
		// if so, make a new Post object and set up its state
		Post *post = [[Post alloc] init];
		[feedPosts addObject:post];
		self.currentElement = post; // ***
		[post release];
		return; // ... and, cut
	}
	
	// *** --> in *either* of these two cases, hang on to the new obj we created as the one that
	//            corresponds to the > element that the parser is currently working on! <
	
	//RE:: new code: reset the buffer, regardless of whether or not this is an element we're
	//   interested in
	self.currentElementData = nil;
}

// parser:foundCharacters:
// XML PARSER DELEGATE METHOD:
// collect data from the gooey center of an XML element
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
	
	// haven't been here already for this current element?
	if (currentElementData == nil) {
		
		// then alloc a buffer to hold the data we're about to start collecting
		self.currentElementData = [[NSMutableString alloc] init];
	}
	
	// tack the new data to the end of the data we already had from this element, if there was any
	// [Note: we may do this multiple times, as the parser may call this method multiple times for the same element!]
	[currentElementData appendString:string];
	
}

// parser:didEndElement:
// XML PARSER DELEGATE METHOD:
// parser has run into the end of an XML element; f.ex. found </dog> tag that matches a <dog> starting tag from earlier
// Again, we only care about the name of the element
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
               namespaceURI:(NSString *)namespaceURI
			   qualifiedName:(NSString *)qName {
	
	// Here's where the eXtreme cleverness / trickery begins:
	//    1. Construct a "selector" (== function name) from the name of the element
    SEL selectorName = NSSelectorFromString(elementName);
	
	//    2. See if the "current element" object knows about such a tag;
	//          in other words, see if an accessor method for an iVar with this name exists --- if it does,
	//          then the object must have a Property with this name and it must have set up a default accessor for it
	//          [--> this is why we HAVE to use XML tag names as the names of iVars if we are interested in picking
	//           up values for these iVars out of the XML "automatically" using this little chuck of code!]
    if ([elementName isEqualToString:@"content:encoded"]) {
		[currentElement setValue:currentElementData forKey:@"content"];
	}
	else if ([currentElement respondsToSelector:selectorName]) {
			
		// 3. Assign the data that we've been collecting from the XML gunk to be the new value of the iVar
		//       in the current obj that matched the name of the XML tag
		[currentElement setValue:currentElementData forKey:elementName];
				
	}
	
	// Release the storage for the buffer and set its address to nil
	[currentElementData release];
	self.currentElementData = nil;
}

// parser:parseErrorOccurred:
// XML PARSER DELEGATE METHOD:
// report a booboo in parsing activity
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
	
	// construct and error message based on the info we got from the parser obj
	NSString *info = [NSString stringWithFormat:@"Error %i, Description: %@, Line: %i, Column: %i",
					  [parseError code],
					  [[parser parserError] localizedDescription],
					  [parser lineNumber],
					  [parser columnNumber]];
	
	// dump the error msg to the console windoe
	NSLog(@"RSS Feed Parse Error: %@", info);
}

#pragma mark -
#pragma mark === instance methods ===
#pragma mark -

// refresh:
// initiate network communication
- (void)refresh {
	
	// make a new request wrapper object around the stored URL for the feed
	self.feedRequest = [ASIHTTPRequest requestWithURL:feedURL];
	
	// clean out all the old stuff before we get new stuff
	[feedPosts removeAllObjects];
	
	// set ME to be the ASIHTTP... obj's delegate
	[feedRequest setDelegate:self];
	
	// don't block the user interface (?) while we wait for new data [do we need this?]
	[feedRequest startAsynchronous];
	
}

@end
